package com.gitee.mp.core.mapper;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ClassUtil;
import com.gitee.mp.annotation.Mapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.lang.NonNull;

import java.util.Objects;
import java.util.Set;

/**
 * @author hongda.li
 */
public class MilvusMapperScanner extends ClassPathBeanDefinitionScanner {

    private final String classPropertyName;

    private final String factoryPropertyName;

    private final MilvusMapperFactory accessorFactory;

    public MilvusMapperScanner(BeanDefinitionRegistry registry,
                               MilvusMapperFactory accessorFactory) {
        super(registry);
        this.accessorFactory = accessorFactory;
        this.classPropertyName = LambdaUtil.getFieldName((Func1<MilvusMapperFactoryBean<?>, Class<?>>) MilvusMapperFactoryBean::getMapper);
        this.factoryPropertyName = LambdaUtil.getFieldName((Func1<MilvusMapperFactoryBean<?>, MilvusMapperFactory>) MilvusMapperFactoryBean::getMapperFactory);
    }

    @NotNull
    @Override
    protected Set<BeanDefinitionHolder> doScan(@NonNull String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);

        this.processBeanDefinitions(beanDefinitionHolders);

        return beanDefinitionHolders;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitionHolders) {
        for (BeanDefinitionHolder holder : beanDefinitionHolders) {
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) holder.getBeanDefinition();
            String className = beanDefinition.getBeanClassName();
            Objects.requireNonNull(className);
            MutablePropertyValues values = beanDefinition.getPropertyValues();
            values.add(classPropertyName, ClassUtil.loadClass(className));
            values.add(factoryPropertyName, accessorFactory);

            beanDefinition.setBeanClass(MilvusMapperFactoryBean.class);
            beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
    }

    @Override
    protected boolean isCandidateComponent(@NonNull AnnotatedBeanDefinition beanDefinition) {
        return true;
    }

    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) {
        AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
        return metadata.isInterface() && metadata.hasAnnotation(Mapper.class.getName());
    }
}
